package org.gjy.m8.stream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

/**
 * @author gjy
 * @version 1.0
 * @since 2025-08-20 05:52:19
 */
public class FunctionChain<T, R> {

    private static final Logger log = LoggerFactory.getLogger(FunctionChain.class);

    private final List<ChainNode<?, ?>> chain = new ArrayList<>();

    private FunctionChain() {
    }

    public static <T, R> FunctionChain<T, R> start() {
        return new FunctionChain<>();
    }

    public <V> FunctionChain<T, R> then(Function<V, ?> function, ErrorStrategy<V> strategy) {
        chain.add(new ChainNode<>(function, strategy));
        return this;
    }

    public R execute(T input) {
        Object current = input;
        for (ChainNode<?, ?> node : chain) {
            current = node.apply(current);
        }
        return (R) current;
    }

    private static class ChainNode<V, U> {
        private final Function<V, U> function;
        private final ErrorStrategy<V> strategy;

        public ChainNode(Function<V, U> function, ErrorStrategy<V> strategy) {
            this.function = function;
            this.strategy = strategy;
        }

        public Object apply(Object input) {
            try {
                return function.apply((V) input);
            } catch (Exception e) {
                return strategy.handle((V) input, e);
            }
        }
    }

    public interface ErrorStrategy<V> {
        Object handle(V input, Exception e);
    }

    // 常用策略封装
    public static <V> ErrorStrategy<V> onErrorContinue() {
        return (input, e) -> {
            log.warn("Step failed, skipping. Input: {}", input, e);
            return input;
        };
    }

    public static <V> ErrorStrategy<V> onErrorBreak() {
        return (input, e) -> {
            log.error("Step failed, terminating. Input: {}", input, e);
            throw new RuntimeException(e);
        };
    }

    public static <V> ErrorStrategy<V> onErrorFallback(Object fallback) {
        return (input, e) -> {
            log.warn("Step failed, using fallback. Input: {}, Fallback: {}", input, fallback, e);
            return fallback;
        };
    }

    public static <V> ErrorStrategy<V> onErrorThrow() {
        return (input, e) -> {
            throw new RuntimeException("Unhandled exception in FunctionChain", e);
        };
    }
}
